package org.krayne.swivel.filter;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collection;

import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JTextField;


/**
 * This is a panel that allows users to create {@code SimpleStringFilter}s.
 * 
 * @author dhsu
 */
public class SimpleStringFilterPanel extends JPanel {    
    private static final long serialVersionUID = 5438103852120242987L;
    
    private final JTextField filterTextField;
    private final JCheckBox caseSensitiveCheckbox;
    private SimpleStringFilter currentFilter;
    private final Collection<SimpleStringFilterListener> filterListeners;
    
    /**
     * Constructs a panel that lets users create {@code SimpleStringFilter}s.
     */
    public SimpleStringFilterPanel() {
        this(SimpleStringFilter.ALL_PASS);
    }
    
    /**
     * Constructs a panel that lets users create {@code SimpleStringFilter}s.
     * Initializes the filter to the one specified.
     * 
     * @param filter an initial filter
     */
    public SimpleStringFilterPanel(SimpleStringFilter filter) {
        super(new BorderLayout());
        this.filterTextField = new JTextField();
        this.caseSensitiveCheckbox = new JCheckBox();
        this.caseSensitiveCheckbox.setToolTipText("AaBbCc");
        this.filterListeners = new ArrayList<SimpleStringFilterListener>();
        this.setFilter(filter);
        
        // notify listeners of a filter change when text field is modified
        this.filterTextField.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                SimpleStringFilter newFilter = createFilterFromForm();
                if(!newFilter.equals(currentFilter)) {
                    currentFilter = newFilter;
                    fireFilterChangeEvent(currentFilter);
                }
            }            
        });
        
        // notify listeners of a filter change when case sensitivity is changed
        this.caseSensitiveCheckbox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                currentFilter = createFilterFromForm();
                fireFilterChangeEvent(currentFilter);
            }
        });
        
        // select filter string on focus, deselect when focus is lost.
        // this lets users easily delete the entire filter quickly.
        this.filterTextField.addFocusListener(new FocusListener() {
            public void focusGained(FocusEvent e) {
                filterTextField.selectAll();
            }

            public void focusLost(FocusEvent e) {
                filterTextField.select(0, 0);
            }
        });
        
        // layout
        this.add(filterTextField, BorderLayout.CENTER);
        this.add(caseSensitiveCheckbox, BorderLayout.EAST);
    }
    
    /**
     * Adds a filter listener to this panel.
     * 
     * @param listener a filter listener
     */
    public void addFilterListener(SimpleStringFilterListener listener) {
        if(!this.filterListeners.contains(listener)) {
            this.filterListeners.add(listener);
        }
    }
    
    /**
     * Removes the specified filter listener from this panel.
     * 
     * @param listener a filter listener
     */
    public void removeFilterListener(SimpleStringFilterListener listener) {
        this.filterListeners.remove(listener);
    }
    
    /**
     * Gets the panel's current filter.
     * 
     * @return the current filter
     */
    public SimpleStringFilter getFilter() {
        return currentFilter;
    }
    
    /**
     * Sets the panel's filter.
     * 
     * @param filter a filter
     */
    public void setFilter(SimpleStringFilter filter) {
        if(filter == null) {
            this.currentFilter = SimpleStringFilter.ALL_PASS;
        } else if(this.currentFilter == null || !this.currentFilter.equals(filter)) {
            this.currentFilter = filter;
        } else {
            // equivalent filter so we don't have to do anything
            return;
        }
        
        this.filterTextField.setText(currentFilter.getFilterString());
        this.caseSensitiveCheckbox.setSelected(currentFilter.isCaseSensitive());
        this.fireFilterChangeEvent(currentFilter);
    }
    
    /**
     * Creates a {@code SimpleStringFilter} using the panel's form values.
     * 
     * @return a filter
     */
    private SimpleStringFilter createFilterFromForm() {
        String filterString = this.filterTextField.getText();
        boolean isCaseSensitive = this.caseSensitiveCheckbox.isSelected();
        return new SimpleStringFilter(filterString, isCaseSensitive);
    }

    /**
     * Notifies listeners of a new simple string filter.
     * 
     * @param newFilter the new filter
     */
    private void fireFilterChangeEvent(SimpleStringFilter newFilter) {
        for(SimpleStringFilterListener listener : filterListeners) {
            listener.filterChanged(newFilter);
        }
    }
    
    /**
     * {@inheritDoc}
     */
    public void setFont(Font font) {
        super.setFont(font);

        if(filterTextField != null) {
            filterTextField.setFont(font);
        }
        
        if(caseSensitiveCheckbox != null) {
            caseSensitiveCheckbox.setFont(font);
        }
    }
}
